package handlers

import (
	"bloggo/bloggo"
	"bloggo/dto"
	"bloggo/service"
	"bloggo/structs"
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/chunanyong/zorm"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"strconv"
	"strings"
	"time"
)

// Articles 文章列表
func Articles(c *gin.Context) *bloggo.R {
	page := zorm.NewPage()
	page.PageNo, _ = strconv.Atoi(c.DefaultQuery("current", "1"))
	page.PageSize, _ = strconv.Atoi(c.DefaultQuery("size", "2"))
	finder := zorm.NewSelectFinder(structs.TbArticleStructTableName, " id, user_id, category_id, article_cover, article_title,SUBSTR( article_content, 1, 500 ) AS article_content, `type`, original_url, is_top, is_delete, status, sortno, create_time, update_time ")
	finder.Append(` WHERE is_delete = 0
								AND status = 1
							ORDER BY
								 is_top DESC,
								 sortno DESC,
								 id DESC`)
	var dtoList []dto.ArticleHomeDTO
	err := zorm.Query(c, finder, &dtoList, page)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	for i, articleStruct := range dtoList {
		id, err := service.FindTbCategoryStructById(c, strconv.Itoa(articleStruct.CategoryId))
		if err != nil {
			return bloggo.FailMessage(err.Error())
		}
		dtoList[i].CategoryName = id.CategoryName
		f_ := zorm.NewFinder().Append(`select
												a.tag_id AS id,
												b.tag_name
											from`).Append(structs.TbArticleTagStructTableName).Append(` 
												a,
												`).Append(structs.TbTagStructTableName).Append(` 
												b
											where
												a.article_id = ?
												and a.tag_id = b.id 
										`, articleStruct.Id)
		err = zorm.Query(c, f_, &dtoList[i].TagDTOList, zorm.NewPage())
		if err != nil {
			return bloggo.FailMessage(err.Error())
		}
	}
	return bloggo.OkData(dtoList)
}

// SaveOrUpdateArticles 保存或更新文章
func SaveOrUpdateArticles(c *gin.Context) *bloggo.R {
	body := c.Request.Body
	all, _ := ioutil.ReadAll(body)
	var reqData dto.PostArticle
	err := json.Unmarshal(all, &reqData)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	var data structs.TbArticleStruct
	data.Id, _ = strconv.Atoi(reqData.Id)
	user := bloggo.GetSessionUser()
	if user != nil {
		data.UserId, _ = strconv.Atoi(user.UserId)
	}
	data.ArticleTitle = reqData.ArticleTitle
	data.ArticleContent = reqData.ArticleContent
	data.ArticleCover = reqData.ArticleCover
	data.OriginalUrl = reqData.OriginalUrl
	data.IsTop = reqData.IsTop
	data.Type = reqData.Type
	data.Status = reqData.Status
	data.CreateTime = time.Now()
	data.UpdateTime = time.Now()
	data.Sortno = service.FindMaxSrotno() + 1

	category, err := service.FindTbCategoryStructByName(c, reqData.CategoryName)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	if category == nil {
		return bloggo.FailMessage("分类不存在！")
	}
	data.CategoryId = category.Id
	for _, v := range reqData.TagNameList {
		var temp structs.TbTagStruct
		temp.TagName = v
		err = service.SaveTbTagStruct(c, &temp)
		if err != nil {
			return bloggo.FailMessage(err.Error())
		}
	}
	idExistTInTable := service.GenIdNotExistTInTable(&(reqData.Id))
	data.Id, _ = strconv.Atoi(reqData.Id)
	_, err = zorm.Transaction(c, func(ctx context.Context) (interface{}, error) {
		ids, err := service.FindeTagIdsByNames(reqData.TagNameList)
		if err != nil {
			return nil, err
		}
		_, err = service.RemoveTagsByArticleId(reqData.Id)
		if err != nil {
			return nil, err
		}
		err = service.SaveArticleTags(reqData.Id, ids)
		if err != nil {
			return nil, err
		}
		if idExistTInTable {
			//更新
			err = service.UpdateTbArticleStruct(ctx, &data)
		} else {
			//新增
			err = service.SaveTbArticleStruct(ctx, &data)
		}
		if err != nil {
			return nil, err
		}
		return nil, nil
	})
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	return bloggo.OkData(reqData)
}

// ArticleById 文章详细信息
func ArticleById(c *gin.Context) *bloggo.R {
	articleId := c.Param("articleId")
	articleDetailInfo := dto.ArticleDetailInfo{}
	// 1.查询推荐文章
	service.FindListRecommendArticles(c, articleId, &articleDetailInfo)
	// 2.查询最新文章
	service.FindNewestArticleList(c, articleId, &articleDetailInfo)
	// 3.查询id对应文章
	finder := zorm.NewSelectFinder(structs.TbArticleStructTableName).Append(" WHERE id=?", articleId)
	_, err := zorm.QueryRow(c, finder, &articleDetailInfo)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	f_ := zorm.NewFinder().Append("select a.tag_id AS id,b.tag_name from").Append(structs.TbArticleTagStructTableName).Append("a,").Append(structs.TbTagStructTableName).Append("b where a.article_id = ? and a.tag_id = b.id", articleId)
	err = zorm.Query(c, f_, &articleDetailInfo.TagDTOList, zorm.NewPage())
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	// 4.更新文章浏览量  updateArticleViewsCount(articleId);
	service.UpdateArticleViewsCount(c, articleId)
	// 5.查询上一篇,下一篇文章
	preFinder := zorm.NewFinder().Append("SELECT id,article_cover,article_title FROM").Append(structs.TbArticleStructTableName).Append("WHERE is_delete=? AND status=? AND id<? ORDER BY id DESC LIMIT 1", 0, dto.PUBLIC, articleId)
	_, err = zorm.QueryRow(c, preFinder, &articleDetailInfo.LastArticle)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	nextFinder := zorm.NewFinder().Append("SELECT id,article_cover,article_title FROM").Append(structs.TbArticleStructTableName).Append("WHERE is_delete=? AND status=? AND id>? ORDER BY id ASC LIMIT 1", 0, dto.PUBLIC, articleId)
	_, err = zorm.QueryRow(c, nextFinder, &articleDetailInfo.NextArticle)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	// 6.获取浏览量和点赞量
	viewCount := bloggo.Zscore(bloggo.ARTICLE_VIEWS_COUNT, []byte(articleId))
	articleDetailInfo.ViewsCount = int(viewCount)
	likeCount, err := bloggo.Hget(bloggo.ARTICLE_LIKE_COUNT, []byte(articleId))
	if err == nil {
		atoi, _ := strconv.Atoi(string(likeCount))
		articleDetailInfo.LikeCount = atoi
	}
	return bloggo.OkData(articleDetailInfo)
}

// ArticleLikeById 点赞或取消点赞文章
func ArticleLikeById(c *gin.Context) *bloggo.R {
	articleId := c.Param("articleId")
	articleIdBytes := []byte(articleId)
	/*address, err := bloggo.GetIpAddress(c.Request)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}*/
	// 判断是否点赞
	articleLikeKey := bloggo.ARTICLE_USER_LIKE + bloggo.GetSessionUser().UserId
	if bloggo.SisMember(articleLikeKey, articleIdBytes) {
		// 点过赞则删除文章id
		bloggo.Srem(articleLikeKey, articleIdBytes)
		// 文章点赞量-1
		bloggo.Hincrby(bloggo.ARTICLE_LIKE_COUNT, articleIdBytes, -1)
	} else {
		// 未点赞则增加文章id
		bloggo.Sadd(articleLikeKey, articleIdBytes)
		// 文章点赞量+1
		bloggo.Hincrby(bloggo.ARTICLE_LIKE_COUNT, articleIdBytes, 1)
	}
	return bloggo.Ok()
}

type Columns struct {
	Field   string `json:"Field"`
	Type    string `json:"Type"`
	Null    string `json:"Null"`
	Key     string `json:"Key"`
	Default string `json:"Default"`
	Extra   string `json:"Extra"`
}

func Test(c *gin.Context) *bloggo.R {
	//user := bloggo.GetSessionUser()
	finder := zorm.NewFinder().Append("SHOW COLUMNS FROM tb_article")
	var columms []Columns
	err := zorm.Query(c, finder, &columms, nil)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	return bloggo.OkData(columms)
}

// Archives 归档
func Archives(c *gin.Context) *bloggo.R {
	page := zorm.NewPage()
	page.PageNo, _ = strconv.Atoi(c.DefaultQuery("current", "1"))
	page.PageSize, _ = strconv.Atoi(c.DefaultQuery("size", "10"))
	finder := zorm.NewFinder()
	finder.Append("SELECT id,article_title,create_time FROM").Append(structs.TbArticleStructTableName).Append("ORDER BY create_time DESC")
	var archiveDTO []dto.ArchiveDTO
	err := zorm.Query(c, finder, &archiveDTO, page)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	return bloggo.OkData(gin.H{
		"recordList": archiveDTO,
		"count":      page.TotalCount,
	})
}

// ArticlesSearch 文章搜索
func ArticlesSearch(c *gin.Context) *bloggo.R {
	page := zorm.NewPage()
	page.PageNo, _ = strconv.Atoi(c.DefaultQuery("current", "1"))
	page.PageSize, _ = strconv.Atoi(c.DefaultQuery("size", "10"))
	keywords := c.DefaultQuery("keywords", "")
	// 判空
	if keywords == "" {
		return bloggo.Ok()
	}
	finder := zorm.NewSelectFinder(structs.TbArticleStructTableName, "id,article_title,article_content")
	finder.Append("WHERE is_delete=? AND status=?", 0, 1)
	finder.Append("AND (article_title LIKE ? OR article_content LIKE ?)", "%"+keywords+"%", "%"+keywords+"%")
	var articles []*structs.TbArticleStruct
	err := zorm.Query(c, finder, &articles, page)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	// 高亮处理
	for i := range articles {
		// 获取关键词第一次出现的位置
		content := articles[i].ArticleContent
		index := strings.Index(content, keywords)
		if index != -1 {
			// 获取关键词前面的文字
			var preIndex int
			if index > 25 {
				preIndex = index - 25
			} else {
				preIndex = 0
			}
			preText := string([]rune(content)[preIndex:index])
			// 获取关键词到后面的文字
			last := index + len(keywords)
			postLength := len(content) - last
			var postIndex int
			if postLength > 175 {
				postIndex = last + 175
			} else {
				postIndex = last + postLength
			}
			postText := string([]rune(content)[index:postIndex])
			// 文章内容高亮
			content = strings.Replace(preText+postText, keywords, bloggo.PRE_TAG+keywords+bloggo.POST_TAG, -1)
		}
		articles[i].ArticleContent = content
		title := articles[i].ArticleTitle
		// 文章标题高亮
		articles[i].ArticleTitle = strings.Replace(title, keywords, bloggo.PRE_TAG+keywords+bloggo.POST_TAG, -1)
	}
	return bloggo.OkData(articles)
}

func UploadImage(c *gin.Context) *bloggo.R {
	link := bloggo.UploadFile(c.Request, "", bloggo.PATH_ARTICLE)
	return bloggo.OkData(link)
}

func AdminArticleById(c *gin.Context) *bloggo.R {
	articleId := c.Param("articleId")
	var data dto.AdminArticleDTO
	finder := zorm.NewSelectFinder(structs.TbArticleStructTableName).Append(" WHERE id=?", articleId)
	_, err := zorm.QueryRow(c, finder, &data)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	finder = zorm.NewFinder().Append("SELECT b.tag_name FROM").Append(structs.TbArticleTagStructTableName).Append("a,")
	finder.Append(structs.TbTagStructTableName).Append("b WHERE a.article_id = ? AND a.tag_id = b.id", articleId)
	err = zorm.Query(c, finder, &data.TagNameList, zorm.NewPage())
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	cate, err := service.FindTbCategoryStructById(c, data.CategoryId)
	data.CategoryName = cate.CategoryName
	return bloggo.OkData(data)
}

// AdminArticles 后台管理文章列表
func AdminArticles(c *gin.Context) *bloggo.R {
	tagId := c.DefaultQuery("tagId", "")
	var articleIds []int
	if len(tagId) != 0 { //先通过文章标签过滤
		finder := zorm.NewFinder().Append("SELECT article_id FROM").Append(structs.TbArticleTagStructTableName)
		finder.Append("WHERE tag_id=?", tagId)
		err := zorm.Query(c, finder, &articleIds, nil)
		if err != nil || len(articleIds) == 0 {
			return bloggo.Ok()
		}
	}
	//获取请求参数
	page := zorm.NewPage()
	page.PageNo, _ = strconv.Atoi(c.DefaultQuery("current", "1"))
	page.PageSize, _ = strconv.Atoi(c.DefaultQuery("size", "10"))
	_type := c.DefaultQuery("type", "")
	status := c.DefaultQuery("status", "")
	isDelete := c.DefaultQuery("isDelete", "")
	categoryId := c.DefaultQuery("categoryId", "")
	keywords := c.DefaultQuery("keywords", "")
	var data []dto.AdminArticleListDTO
	finder := zorm.NewFinder()
	finder.Append("SELECT a.id,a.article_cover,a.article_title,a.create_time,b.category_name,a.type,a.is_top,a.is_delete,a.status,a.sortno FROM")
	finder.Append(structs.TbArticleStructTableName)
	finder.Append("a LEFT JOIN").Append(structs.TbCategoryStructTableName).Append("b ON").Append("a.category_id=b.id")
	finder.Append("WHERE 1=1")
	//拼接搜索条件
	if len(_type) != 0 {
		finder.Append("AND a.type=?", _type)
	}
	if len(status) != 0 {
		finder.Append("AND a.status=?", status)
	}
	if len(isDelete) != 0 {
		finder.Append("AND is_delete=?", isDelete)
	}
	if len(categoryId) != 0 {
		finder.Append("AND a.category_id=?", categoryId)
	}
	if len(articleIds) != 0 {
		finder.Append("AND a.id IN (?)", articleIds)
	}
	if len(keywords) != 0 {
		finder.Append("AND a.article_title LIKE ?", fmt.Sprint("%", keywords, "%"))
	}
	finder.Append("ORDER BY a.id DESC")
	err := zorm.Query(c, finder, &data, page)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	if len(data) > 0 {
		for i := range data {
			item := &data[i]
			finder = zorm.NewFinder().Append("SELECT a.id,a.tag_name FROM").Append(structs.TbTagStructTableName)
			finder.Append("a LEFT JOIN").Append(structs.TbArticleTagStructTableName).Append("b ON a.id=b.tag_id")
			finder.Append("WHERE b.article_id=?", item.Id)
			err = zorm.Query(c, finder, &(item.TagDTOList), nil)
			if err != nil {
				return bloggo.FailMessage(err.Error())
			}
			// 获取浏览量和点赞量
			data[i].ViewsCount = int(bloggo.Zscore(bloggo.ARTICLE_VIEWS_COUNT, []byte(item.Id)))
			likeCount, _ := bloggo.Hget(bloggo.ARTICLE_LIKE_COUNT, []byte(item.Id))
			item.LikeCount, _ = strconv.Atoi(string(likeCount))
		}
	}
	return bloggo.OkData(data)
}

// AdminLogicDeleteArticles 后台管理更新文章删除状态
func AdminLogicDeleteArticles(c *gin.Context) *bloggo.R {
	var reqData dto.ArticleDelDTO
	bytes, _ := ioutil.ReadAll(c.Request.Body)
	err := json.Unmarshal(bytes, &reqData)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	if len(reqData.IdList) <= 0 {
		return bloggo.Ok()
	}
	_, err = zorm.Transaction(c, func(ctx context.Context) (interface{}, error) {
		for _, v := range reqData.IdList {
			finder := zorm.NewUpdateFinder(structs.TbArticleStructTableName)
			finder.Append("is_delete=?,update_time=?", reqData.IsDelete, time.Now())
			finder.Append("WHERE id=?", v)
			_, err = zorm.UpdateFinder(ctx, finder)
			if err != nil {
				return nil, err
			}
		}
		return nil, nil
	})
	if err != nil {
		bloggo.Log(err.Error())
		return bloggo.FailMessage(err.Error())
	}
	return bloggo.Ok()
}

// AdminDeleteArticles 后台管理删除文章
func AdminDeleteArticles(c *gin.Context) *bloggo.R {
	var delIdList []int
	bytes, _ := ioutil.ReadAll(c.Request.Body)
	err := json.Unmarshal(bytes, &delIdList)
	if err != nil {
		return bloggo.FailMessage(err.Error())
	}
	if len(delIdList) <= 0 {
		return bloggo.Ok()
	}
	_, err = zorm.Transaction(c, func(ctx context.Context) (interface{}, error) {
		for _, v := range delIdList {
			finder := zorm.NewDeleteFinder(structs.TbArticleStructTableName)
			finder.Append("WHERE id=?", v)
			_, err = zorm.UpdateFinder(ctx, finder)
			if err != nil {
				return nil, err
			}
		}
		return nil, nil
	})
	if err != nil {
		bloggo.Log(err.Error())
		return bloggo.FailMessage(err.Error())
	}
	return bloggo.Ok()
}
